#include <iostream>
#include <boost/asio.hpp>

//异步echo服务器
/*
该demo示例为仿照asio官网编写的，其中存在隐患，就是当服务器即将发送数据前(调用async_write前)，此刻客户端中断，
服务器此时调用async_write会触发发送回调函数，判断ec为非0进而执行delete this逻辑回收session。但要注意的是客户端关闭后，
在tcp层面会触发读就绪事件，服务器会触发读事件回调函数。在读事件回调函数中判断错误码ec为非0，进而再次执行delete操作，
从而造成二次析构，这是极度危险的。
*/
class Session
{
public:
    Session(boost::asio::io_context &ioc):socket_(ioc){};


    void Start();
    boost::asio::ip::tcp::socket& Socket(){return socket_;};
private:
    boost::asio::ip::tcp::socket socket_;

    enum{max_length = 1024};
    char data_[max_length];

    void read_handle(const boost::system::error_code error,std::size_t bytes_transferred);
    void write_handle(const boost::system::error_code error,std::size_t bytes_transferred);
};

void Session::Start(){
    memset(data_,0,max_length);
    socket_.async_read_some(boost::asio::buffer(data_,max_length),
    std::bind(&Session::read_handle,this,std::placeholders::_1,std::placeholders::_2));
}

void Session::read_handle(const boost::system::error_code error,std::size_t bytes_transferred)
{
    if (!error)
    {
        std::cout << "receive data:" << data_ << std::endl;
        socket_.async_write_some(boost::asio::buffer(data_,bytes_transferred),
        std::bind(&Session::write_handle,this,std::placeholders::_1,std::placeholders::_2));
    }
    else
    {
        std::cout << "read error\n" << "error.value:" <<error.value() << "error.message:" << error.message() <<std::endl;
        delete this;
    }
    
}

void Session::write_handle(const boost::system::error_code error,std::size_t bytes_transferred)
{
    if (!error)
    {
        memset(data_,0,max_length);
        socket_.async_read_some(boost::asio::buffer(data_,max_length),
        std::bind(&Session::read_handle,this,std::placeholders::_1,std::placeholders::_2));
    }
    else
    {
        std::cout << "write error\n" << "error.value:" <<error.value() << "error.message:" << error.message() <<std::endl;
        delete this;
    }
    
}

class Server {
public:
    Server(boost::asio::io_context& ioc, short port);
private:
    void start_accept();
    void handle_accept(Session* new_session, const boost::system::error_code& error);
    boost::asio::io_context &_ioc;
    boost::asio::ip::tcp::acceptor _acceptor;
};

Server::Server(boost::asio::io_context& ioc, short port) :_ioc(ioc),
    _acceptor(ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::any(), port).protocol()){
    _acceptor.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));
    _acceptor.listen();
    start_accept();
}

void Server::start_accept() {
    Session* new_session = new Session(_ioc);
    _acceptor.async_accept(new_session->Socket(),
    std::bind(&Server::handle_accept, this, new_session, std::placeholders::_1));
}
void Server::handle_accept(Session* new_session, const boost::system::error_code& error) {

    if (!error) {
        new_session->Start();
    }
    else {
        delete new_session;
    }
    start_accept();
}

int main(int argc, char const *argv[])
{
    boost::asio::io_context ioc;
    unsigned short port = 10086;
    Server server(ioc,port);
    ioc.run();
    
    return 0;
}
